package com.zhangdi.dwtools.tree;

import com.zhangdi.dwtools.collection.FastList;
import com.zhangdi.dwtools.tree.Node;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class TreeUtil {
    private static String IS_CHILD = "isChild";
    private static String NODE_ID_METHOD_NAME = "getId";
    private static String PARENT_METHOD_NAME = "getPid";
    private static String CHILDREN_METHOD_NAME = "getChildren";
    private static String ALL_CHILDREN_METHOD_NAME = "getAllChildren";

    public static <K, V extends Node> Map<K, V> list2Map(FastList<V> list, String keyMethodName, Class<V> c) {
        Map<K, V> map = new HashMap<>();
        if (list != null) {
            try {
                Method methodGetKey = c.getMethod(keyMethodName);
                for (int i = 0; i < list.size(); i++) {
                    V value = list.get(i);
                    K key = (K) methodGetKey.invoke(list.get(i));
                    map.put(key, value);
                }
            } catch (Exception e) {
                throw new IllegalArgumentException("找不到指定的属性");
            }
        }

        return map;
    }

    public static <K, V extends Node> FastList<V> list2tree(FastList<V> result, Class<V> c) {
        Map<K, V> treeNodesMap = list2Map(result, NODE_ID_METHOD_NAME, c);

        V treeNode = null;
        V parentTreeNode = null;
        Iterator<V> iter = treeNodesMap.values().iterator();
        while (iter.hasNext()) {
            treeNode = iter.next();
            long parentId = 0;
            try {
                parentId = (long) treeNode.getClass().getMethod(PARENT_METHOD_NAME).invoke(treeNode);
            } catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
                e.printStackTrace();
            }

            if (!treeNodesMap.containsKey(parentId)) {
                continue;
            }

            parentTreeNode = treeNodesMap.get(parentId);
            try {
                treeNode.getClass().getField(IS_CHILD).setBoolean(treeNode, true);
            } catch (IllegalAccessException | NoSuchFieldException e) {
                e.printStackTrace();
            }

            FastList<V> children = null;
            try {
                children = (FastList<V>) parentTreeNode.getClass().getMethod(CHILDREN_METHOD_NAME).invoke(parentTreeNode);
            } catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
                e.printStackTrace();
            }
            children.add(treeNode);
        }

        FastList<V> treeNodesList = new FastList<>(Node.class);
        if (treeNodesMap.size() == 0) {
            return treeNodesList;
        }

        V node = null;
        Iterator<V> treesIter = treeNodesMap.values().iterator();
        while (treesIter.hasNext()) {
            node = treesIter.next();
            boolean isChild = false;
            try {
                isChild = node.getClass().getField(IS_CHILD).getBoolean(node);
            } catch (IllegalAccessException | NoSuchFieldException e) {
                e.printStackTrace();
            }
            if (!isChild) {
                treeNodesList.add(node);
                try {
                    treeNodesList.addAll((FastList<V>) node.getClass().getMethod(ALL_CHILDREN_METHOD_NAME).invoke(node));
                } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                    e.printStackTrace();
                }
            }
        }

        for (int i = treeNodesList.size() - 1; i >= 0; --i) {
            boolean isChild = false;
            try {
                isChild = treeNodesList.get(i).getClass().getField(IS_CHILD).getBoolean(treeNodesList.get(i));
            } catch (IllegalAccessException | NoSuchFieldException e) {
                e.printStackTrace();
            }
            if (isChild) {
                treeNodesList.remove(i);
            }
        }

        return treeNodesList;
    }

    public static <K, V extends Node> FastList<V> list2tree(FastList<V> result, Class<V> c, String keyMethodName) {
        Map<K, V> treeNodesMap = list2Map(result, keyMethodName, c);

        V treeNode = null;
        V parentTreeNode = null;
        Iterator<V> iter = treeNodesMap.values().iterator();
        while (iter.hasNext()) {
            treeNode = iter.next();
            long parentId = 0;
            try {
                parentId = (long) treeNode.getClass().getMethod(PARENT_METHOD_NAME).invoke(treeNode);
            } catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
                e.printStackTrace();
            }

            if (!treeNodesMap.containsKey(parentId)) {
                continue;
            }

            parentTreeNode = treeNodesMap.get(parentId);
            try {
                Field isChildField = treeNode.getClass().getDeclaredField(IS_CHILD);
                if (isChildField == null) {
                    isChildField = treeNode.getClass().getField(IS_CHILD);
                }
                isChildField.setBoolean(treeNode, true);
            } catch (IllegalAccessException | NoSuchFieldException e) {
                e.printStackTrace();
            }

            FastList<V> children = null;
            try {
                children = (FastList<V>) parentTreeNode.getClass().getMethod(CHILDREN_METHOD_NAME).invoke(parentTreeNode);
            } catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
                e.printStackTrace();
            }
            children.add(treeNode);
        }

        FastList<V> treeNodesList = new FastList<>(Node.class);
        if (treeNodesMap.size() == 0) {
            return treeNodesList;
        }

        V node = null;
        Iterator<V> treesIter = treeNodesMap.values().iterator();
        while (treesIter.hasNext()) {
            node = treesIter.next();
            boolean isChild = false;
            try {
                isChild = node.getClass().getField(IS_CHILD).getBoolean(node);
            } catch (IllegalAccessException | NoSuchFieldException e) {
                e.printStackTrace();
            }
            if (!isChild) {
                treeNodesList.add(node);
                try {
                    treeNodesList.addAll((FastList<V>) node.getClass().getMethod(ALL_CHILDREN_METHOD_NAME).invoke(node));
                } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                    e.printStackTrace();
                }
            }
        }

        for (int i = treeNodesList.size() - 1; i >= 0; --i) {
            boolean isChild = false;
            try {
                isChild = treeNodesList.get(i).getClass().getField(IS_CHILD).getBoolean(treeNodesList.get(i));
            } catch (IllegalAccessException | NoSuchFieldException e) {
                e.printStackTrace();
            }
            if (isChild) {
                treeNodesList.remove(i);
            }
        }

        return treeNodesList;
    }

    public static <K, V extends Node> FastList<V> list2treeSort(FastList<V> result, Class<V> c) {
        Map<K, V> treeNodesMap = list2Map(result, NODE_ID_METHOD_NAME, c);

        V treeNode = null;
        V parentTreeNode = null;
        Iterator<V> iter = treeNodesMap.values().iterator();
        while (iter.hasNext()) {
            treeNode = iter.next();
            long parentId = 0;
            try {
                parentId = (long) treeNode.getClass().getMethod(PARENT_METHOD_NAME).invoke(treeNode);
            } catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
                e.printStackTrace();
            }

            if (!treeNodesMap.containsKey(parentId)) {
                continue;
            }

            parentTreeNode = treeNodesMap.get(parentId);
            try {
                treeNode.getClass().getField(IS_CHILD).setBoolean(treeNode, true);
            } catch (IllegalAccessException | NoSuchFieldException e) {
                e.printStackTrace();
            }

            FastList<V> children = null;
            try {
                children = (FastList<V>) parentTreeNode.getClass().getMethod(CHILDREN_METHOD_NAME).invoke(parentTreeNode);
            } catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
                e.printStackTrace();
            }
            children.add(treeNode);
        }

        FastList<V> treeNodesList = new FastList<>(Node.class);
        if (treeNodesMap.size() == 0) {
            return treeNodesList;
        }

        V node = null;
        Iterator<V> treesIter = treeNodesMap.values().iterator();
        while (treesIter.hasNext()) {
            node = treesIter.next();
            boolean isChild = false;
            try {
                isChild = node.getClass().getField(IS_CHILD).getBoolean(node);
            } catch (IllegalAccessException | NoSuchFieldException e) {
                e.printStackTrace();
            }
            if (!isChild) {
                treeNodesList.add(node);
                try {
                    treeNodesList.addAll((FastList<V>) node.getClass().getMethod(ALL_CHILDREN_METHOD_NAME).invoke(node));
                } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                    e.printStackTrace();
                }
            }
        }

        for (int i = treeNodesList.size() - 1; i >= 0; --i) {
            boolean isChild = false;
            try {
                isChild = treeNodesList.get(i).getClass().getField(IS_CHILD).getBoolean(treeNodesList.get(i));
            } catch (IllegalAccessException | NoSuchFieldException e) {
                e.printStackTrace();
            }
            if (isChild) {
                treeNodesList.remove(i);
            }
        }

        treeNodesList.sort(comparator);

        for (V v : treeNodesList) {
            FastList<V> children = null;
            try {
                children = (FastList<V>) v.getClass().getMethod(CHILDREN_METHOD_NAME).invoke(v);
            } catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
                e.printStackTrace();
            }

            if (children == null || children.size() == 0) {
                continue;
            }

            children.sort(comparator);
        }

        return treeNodesList;
    }

    private static Comparator comparator = (o1, o2) -> {
        int v1 = 0;
        int v2 = 0;
        try {
            v1 = (int) o1.getClass().getMethod("getSort").invoke(o1);
            v2 = (int) o2.getClass().getMethod("getSort").invoke(o2);
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return v1 - v2;
    };
}
